#include "wx/wxprec.h"
#include "wx/fontmap.h"

#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/log.h"
#include "wx/intl.h"
#include "wx/msgdlg.h"
#include "wx/choicdlg.h"
#endif // PCH

#if wxUSE_CONFIG
#include "wx/config.h"
#endif // wxUSE_CONFIG

#include "wx/fmappriv.h"
#include "wx/fontutil.h"
#include "wx/fontdlg.h"
#include "wx/encinfo.h"
#include "wx/encconv.h"


// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------

// the config paths we use
#if wxUSE_CONFIG

static const wxChar* FONTMAPPER_FONT_FROM_ENCODING_PATH = wxT( "Encodings" );
static const wxChar* FONTMAPPER_FONT_DONT_ASK = wxT( "none" );

#endif // wxUSE_CONFIG

// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------

// it may happen that while we're showing a dialog asking the user about
// something, another request for an encoding mapping arrives: in this case it
// is best to not do anything because otherwise we risk to enter an infinite
// loop so we create an object of this class on stack to test for this in all
// interactive functions
class ReentrancyBlocker {
  public:
    ReentrancyBlocker( bool& flag ) : m_flagOld( flag ), m_flag( flag ) { m_flag = true; }
    ~ReentrancyBlocker() { m_flag = m_flagOld; }

  private:
    bool m_flagOld;
    bool& m_flag;

    DECLARE_NO_COPY_CLASS( ReentrancyBlocker )
};

// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// ctor and dtor
// ----------------------------------------------------------------------------

wxFontMapper::wxFontMapper() {
  m_windowParent = NULL;
}

wxFontMapper::~wxFontMapper() {
}

/* static */
wxFontMapper *wxFontMapper::Get() {
  wxFontMapperBase *fontmapper = wxFontMapperBase::Get();
  wxASSERT_MSG( !fontmapper->IsDummy(),
                wxT( "GUI code requested a wxFontMapper but we only have a wxFontMapperBase." ) );
  return ( wxFontMapper * )fontmapper;
}

wxFontEncoding
wxFontMapper::CharsetToEncoding( const wxString& charset, bool interactive ) {
  int encoding = wxFontMapperBase::NonInteractiveCharsetToEncoding( charset );
  if( encoding == wxFONTENCODING_UNKNOWN ) {
    encoding = wxFONTENCODING_SYSTEM;
  }
  #if wxUSE_CHOICEDLG
  else if( ( encoding == wxFONTENCODING_SYSTEM ) && interactive ) {
    // prepare the dialog data
    // the dialog title
    wxString title( m_titleDialog );
    if( !title ) {
      title << wxTheApp->GetAppName() << _( ": unknown charset" );
    }
    // the message
    wxString msg;
    msg.Printf( _( "The charset '%s' is unknown. You may select\nanother charset to replace it with or choose\n[Cancel] if it cannot be replaced" ), charset.c_str() );
    // the list of choices
    const size_t count = GetSupportedEncodingsCount();
    wxString *encodingNamesTranslated = new wxString[count];
    for( size_t i = 0; i < count; i++ ) {
      encodingNamesTranslated[i] = GetEncodingDescription( GetEncoding( i ) );
    }
    // the parent window
    wxWindow *parent = m_windowParent;
    if( !parent ) {
      parent = wxTheApp->GetTopWindow();
    }
    int n = wxGetSingleChoiceIndex( msg, title,
                                    count,
                                    encodingNamesTranslated,
                                    parent );
    delete [] encodingNamesTranslated;
    if( n != -1 ) {
      encoding = GetEncoding( n );
    }
    #if wxUSE_CONFIG && wxUSE_FILECONFIG
    // save the result in the config now
    wxFontMapperPathChanger path( this, FONTMAPPER_CHARSET_PATH );
    if( path.IsOk() ) {
      wxConfigBase *config = GetConfig();
      // remember the alt encoding for this charset -- or remember that
      // we don't know it
      long value = n == -1 ? ( long )wxFONTENCODING_UNKNOWN : ( long )encoding;
      if( !config->Write( charset, value ) ) {
        wxLogError( _( "Failed to remember the encoding for the charset '%s'." ), charset.c_str() );
      }
    }
    #endif // wxUSE_CONFIG
  }
  #else
  wxUnusedVar( interactive );
  #endif // wxUSE_CHOICEDLG
  return ( wxFontEncoding )encoding;
}

bool wxFontMapper::TestAltEncoding( const wxString& configEntry,
                                    wxFontEncoding encReplacement,
                                    wxNativeEncodingInfo *info ) {
  if( wxGetNativeFontEncoding( encReplacement, info ) &&
      wxTestFontEncoding( *info ) ) {
    #if wxUSE_CONFIG && wxUSE_FILECONFIG
    // remember the mapping in the config
    wxFontMapperPathChanger path( this, FONTMAPPER_FONT_FROM_ENCODING_PATH );
    if( path.IsOk() ) {
      GetConfig()->Write( configEntry, info->ToString() );
    }
    #else
    wxUnusedVar( configEntry );
    #endif // wxUSE_CONFIG
    return true;
  }
  return false;
}

bool wxFontMapper::GetAltForEncoding( wxFontEncoding encoding,
                                      wxNativeEncodingInfo *info,
                                      const wxString& facename,
                                      bool interactive ) {
  #if wxUSE_GUI
  static bool s_inGetAltForEncoding = false;
  if( interactive && s_inGetAltForEncoding ) {
    return false;
  }
  ReentrancyBlocker blocker( s_inGetAltForEncoding );
  #endif // wxUSE_GUI
  wxCHECK_MSG( info, false, wxT( "bad pointer in GetAltForEncoding" ) );
  info->facename = facename;
  if( encoding == wxFONTENCODING_DEFAULT ) {
    encoding = wxFont::GetDefaultEncoding();
  }
  if( encoding == wxFONTENCODING_SYSTEM ) {
    wxLogFatalError( _( "can't load any font, aborting" ) );
    // wxLogFatalError doesn't return
  }
  wxString configEntry,
           encName = GetEncodingName( encoding );
  if( !facename.empty() ) {
    configEntry = facename + _T( "_" );
  }
  configEntry += encName;
  #if wxUSE_CONFIG && wxUSE_FILECONFIG
  // do we have a font spec for this encoding?
  wxString fontinfo;
  wxFontMapperPathChanger path( this, FONTMAPPER_FONT_FROM_ENCODING_PATH );
  if( path.IsOk() ) {
    fontinfo = GetConfig()->Read( configEntry );
  }
  if( fontinfo == FONTMAPPER_FONT_DONT_ASK ) {
    interactive = false;
  } else { // use the info entered the last time
    if( !fontinfo.empty() && !facename.empty() ) {
      fontinfo = GetConfig()->Read( encName );
    }
    if( !fontinfo.empty() ) {
      if( info->FromString( fontinfo ) ) {
        if( wxTestFontEncoding( *info ) ) {
          // ok, got something
          return true;
        }
      } else {
        wxLogDebug( wxT( "corrupted config data: string '%s' is not a valid font encoding info" ),
                    fontinfo.c_str() );
      }
    }
    //else: there is no information in config about this encoding
  }
  #endif // wxUSE_CONFIG
  wxFontEncodingArray equiv = wxEncodingConverter::GetAllEquivalents( encoding );
  size_t count = equiv.GetCount();
  bool foundEquivEncoding = false;
  wxFontEncoding equivEncoding = wxFONTENCODING_SYSTEM;
  if( count ) {
    for( size_t i = 0; i < count && !foundEquivEncoding; i++ ) {
      // don't test for encoding itself, we already know we don't have it
      if( equiv[i] == encoding ) {
        continue;
      }
      if( TestAltEncoding( configEntry, equiv[i], info ) ) {
        equivEncoding = equiv[i];
        foundEquivEncoding = true;
      }
    }
  }
  #if wxUSE_FONTDLG
  if( interactive ) {
    wxString title( m_titleDialog );
    if( !title ) {
      title << wxTheApp->GetAppName() << _( ": unknown encoding" );
    }
    wxString encDesc = GetEncodingDescription( encoding ),
             msg;
    if( foundEquivEncoding ) {
      msg.Printf( _( "No font for displaying text in encoding '%s' found,\nbut an alternative encoding '%s' is available.\nDo you want to use this encoding (otherwise you will have to choose another one)?" ),
                  encDesc.c_str(), GetEncodingDescription( equivEncoding ).c_str() );
    } else {
      msg.Printf( _( "No font for displaying text in encoding '%s' found.\nWould you like to select a font to be used for this encoding\n(otherwise the text in this encoding will not be shown correctly)?" ),
                  encDesc.c_str() );
    }
    int answer = foundEquivEncoding ? wxNO : wxYES;
    if( wxMessageBox( msg, title,
                      wxICON_QUESTION | wxYES_NO,
                      m_windowParent ) == answer ) {
      wxFontData data;
      data.SetEncoding( encoding );
      data.EncodingInfo() = *info;
      wxFontDialog dialog( m_windowParent, data );
      if( dialog.ShowModal() == wxID_OK ) {
        wxFontData retData = dialog.GetFontData();
        *info = retData.EncodingInfo();
        info->encoding = retData.GetEncoding();
        #if wxUSE_CONFIG && wxUSE_FILECONFIG
        wxFontMapperPathChanger path2( this, FONTMAPPER_FONT_FROM_ENCODING_PATH );
        if( path2.IsOk() ) {
          GetConfig()->Write( configEntry, info->ToString() );
        }
        #endif
        return true;
      }
    } else {
      #if wxUSE_CONFIG && wxUSE_FILECONFIG
      wxFontMapperPathChanger path2( this,
                                     FONTMAPPER_FONT_FROM_ENCODING_PATH );
      if( path2.IsOk() ) {
        GetConfig()->Write( configEntry, foundEquivEncoding ? info->ToString().c_str() : FONTMAPPER_FONT_DONT_ASK );
      }
      #endif
    }
  }
  #else
  wxUnusedVar( equivEncoding );
  #endif
  return foundEquivEncoding;
}

bool wxFontMapper::GetAltForEncoding( wxFontEncoding encoding, wxFontEncoding *encodingAlt, const wxString& facename, bool interactive ) {
  wxCHECK_MSG( encodingAlt, false,
               _T( "wxFontEncoding::GetAltForEncoding(): NULL pointer" ) );
  wxNativeEncodingInfo info;
  if( !GetAltForEncoding( encoding, &info, facename, interactive ) ) {
    return false;
  }
  *encodingAlt = info.encoding;
  return true;
}

bool wxFontMapper::IsEncodingAvailable( wxFontEncoding encoding, const wxString& facename ) {
  wxNativeEncodingInfo info;
  if( !wxGetNativeFontEncoding( encoding, &info ) ) {
    return false;
  }
  info.facename = facename;
  return wxTestFontEncoding( info );
}
